package com.hvgroup.modules.sanic.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.hvgroup.modules.sanic.domain.dto.LlmDatasetDto;
import com.hvgroup.modules.sanic.domain.dto.LlmDatasetsTpmlDto;
import com.hvgroup.modules.sanic.domain.po.LlmDataset;
import com.hvgroup.modules.sanic.domain.po.LlmDatasetsTpml;
import com.hvgroup.modules.sanic.domain.po.LlmModelConfig;
import com.hvgroup.modules.sanic.domain.po.LlmRolePermission;
import com.hvgroup.modules.sanic.domain.qo.CPReqBody;
import com.hvgroup.modules.sanic.repository.*;
import com.hvgroup.modules.sanic.service.CommDatasetService;
import com.hvgroup.modules.sanic.service.impl.chat.EventStrategy;
import com.hvgroup.modules.sanic.service.mapstruct.LlmDatasetsMapper;
import com.hvgroup.modules.sanic.service.mapstruct.LlmDatasetsTpmlMapper;
import com.hvgroup.utils.PageResult;
import com.hvgroup.utils.PageUtil;
import com.hvgroup.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class CommDatasetServiceImpl implements CommDatasetService {

    private final LlmAppRepository llmAppRepository;
    private final LlmModelConfigRepository llmModelConfigRepository;
    private final LlmModelConfigApiRepository llmModelConfigApiRepository;

    private final LlmDatasetsRepository llmDatasetsRepository;
    private final LlmDatasetsTpmlRepository llmDatasetsTpmlRepository;

    private final LlmDatasetsMapper llmDatasetsMapper;
    private final LlmDatasetsTpmlMapper llmDatasetsTpmlMapper;

    private final LlmRolePermissionRepository llmRolePermissionRepository;

    private final EventStrategy eventStrategy;


    @Override
    public Mono<JsonNode> postDataset(CPReqBody cpReqBody) {

        Mono<LlmModelConfig> configMono = Mono.fromCallable(() ->
                llmModelConfigRepository.findById(cpReqBody.getPlatformId())
                        .orElseThrow(() -> new RuntimeException("请选择应用!"))
        ).subscribeOn(Schedulers.boundedElastic());

        // TODO 待优化:各类型分割
        return configMono.flatMap(platform -> eventStrategy.getExecute(platform.getPlatformType())
                .buildDsetApi(platform, cpReqBody));
    }

    @Override
    public PageResult<LlmDatasetDto> getDatasets(CPReqBody cpReqBody) {

        List<LlmRolePermission> use_list = llmRolePermissionRepository.findByUserId(cpReqBody.getUserId(), cpReqBody.getPlatformId());

        List<Long> dsId = use_list.stream()
                .flatMap(o -> Arrays.stream(StringUtils.defaultString(o.getDatasetsId(), "").split(",")))
                .map(String::trim)
                .filter(StringUtils::isNotBlank)
                .filter(s -> s.matches("\\d+"))
                .map(Long::valueOf)
                .collect(Collectors.toList());

        Map<String, Object> jsonObject = cpReqBody.getBody();
        String name = (String) jsonObject.get("name");
        Page<LlmDataset> page = llmDatasetsRepository.findAll(((root, criteriaQuery, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            // 模糊匹配 name
            if (StringUtils.isNotBlank(name)) {
                predicates.add(cb.like(root.get("name"), "%" + name + "%"));
            }

            CriteriaBuilder.In<Long> inClause = cb.in(root.get("id"));
            dsId.forEach(inClause::value);
            predicates.add(inClause);

            return cb.and(predicates.toArray(new Predicate[0]));

        }), cpReqBody.getPageable());

        return PageUtil.toPage(page.map(llmDatasetsMapper::toDto));
    }

    @Override
    public PageResult<LlmDatasetsTpmlDto> getDatasetsTpml(CPReqBody cpReqBody) {
        Map<String, Object> jsonObject = cpReqBody.getBody();
        String name = (String) jsonObject.get("name");

        LlmDataset llmDataset = llmDatasetsRepository.findById(Long.valueOf(cpReqBody.getLevelId_2())).orElseThrow(() -> new RuntimeException("<UNK>!"));

        Page<LlmDatasetsTpml> page = llmDatasetsTpmlRepository.findAll(((root, criteriaQuery, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 模糊匹配 name
            if (StringUtils.isNotEmpty(name)) {
                predicates.add(cb.like(root.get("docName"), "%" + name + "%"));
            }
            predicates.add(cb.equal(root.get("datasetsId"), llmDataset.getDatasetsId()));
            return cb.and(predicates.toArray(new Predicate[0]));

        }), cpReqBody.getPageable());

        return PageUtil.toPage(page.map(llmDatasetsTpmlMapper::toDto));
    }

    @Override
    public Mono<Object> deleteDocument(CPReqBody cpReqBody) {
        Mono<LlmModelConfig> configMono = Mono.fromCallable(() ->
                llmModelConfigRepository.findById(cpReqBody.getPlatformId())
                        .orElseThrow(() -> new RuntimeException("请选择应用!"))
        ).subscribeOn(Schedulers.boundedElastic());

        return configMono.flatMap(platform -> eventStrategy.getExecute(platform.getPlatformType())
                .deleteDatasetsTpml(platform, cpReqBody));
    }

    @Override
    public Object uploadDocument(CPReqBody cpReqBody) {
        LlmModelConfig platform = llmModelConfigRepository.findById(cpReqBody.getPlatformId())
                        .orElseThrow(() -> new RuntimeException("请选择应用!"));

        return eventStrategy.getExecute(platform.getPlatformType())
                .uploadDatasetsTpml(platform, cpReqBody);
    }
}
